﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;

namespace BaseTool
{
    /// <summary>
    /// 文件读写类
    /// </summary>
    public static class FileTool
    {
        #region 文本操作
        /// <summary>
        /// 写入文本数据
        /// </summary>
        /// <param showName="fileName">文本文件路径</param>
        /// <param showName="info">要写入的信息</param>
        /// <param showName="mode">确定已有文件时时追加还是覆盖</param>
        /// <param showName="encoding">写入字符编码格式，默认ASCII</param>
        public static bool WriteTxt(string fileName, string info, FileMode mode = FileMode.Create, Encoding encoding = null)
        {
            try
            {
                if (encoding == null)
                    encoding = Encoding.ASCII;
                DirectoryInfo dirinfo = Directory.GetParent(fileName);
                if (!dirinfo.Exists)
                    dirinfo.Create();
                using (FileStream fs = new FileStream(fileName, mode))
                {
                    using (StreamWriter sw = new StreamWriter(fs, encoding))
                    {
                        sw.WriteLine(info);
                        sw.Flush();
                        sw.Close();
                        fs.Close();
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return false;
            }
        }

        /// <summary>
        /// 读取文本数据
        /// </summary>
        /// <param showName="fileName"></param>
        /// <param showName="mode"></param>
        /// <param showName="encoding">写入字符编码格式，默认ASCII</param>
        /// <returns></returns>
        public static string ReadTxt(string fileName, FileMode mode = FileMode.Open, Encoding encoding = null)
        {
            if (!File.Exists(fileName))
                return "";
            try
            {
                StringBuilder SB = new StringBuilder();

                if (encoding == null)
                    encoding = Encoding.ASCII;
                using (FileStream fs = new FileStream(fileName, mode))
                {
                    using (StreamReader sr = new StreamReader(fs, encoding))
                    {
                        while (!sr.EndOfStream)
                        {
                            SB.AppendLine(sr.ReadLine());
                        }
                        sr.Close();
                        fs.Close();
                    }
                }
                return SB.ToString();
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return "";
            }
        }

        /// <summary>
        /// 将文件夹及其内容复制到新位置
        /// </summary>
        /// <param name="sourcePath">要复制的文件路径</param>
        /// <param name="destPath">指向 sourceDirName 的新位置的路径</param>
        /// <param name="overwrite">允许覆盖同名的文件</param>
        public static void CopyDirectory(string sourcePath, string destPath, bool overwrite = true)
        {
            try
            {
                if (!Directory.Exists(destPath))
                    Directory.CreateDirectory(destPath);

                //文件复制
                string[] files = Directory.GetFiles(sourcePath);
                foreach (string file in files)
                {
                    File.Copy(file, destPath + "\\" + Path.GetFileName(file), overwrite);
                }

                //子目录递归复制
                string[] dirs = Directory.GetDirectories(sourcePath);
                foreach (string dir in dirs)
                {
                    CopyDirectory(dir, destPath + "\\" + Path.GetFileName(dir), overwrite);
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
            }
        }

        #endregion

        #region 序列化
        /// <summary>
        /// 保存数据到XML文件
        /// </summary>
        /// <param name="obj">要保存的数据</param>
        /// <param name="fileName">保存文件的路径</param>
        public static bool WriteXML(string fileName, object obj)
        {
            if (obj == null)
                throw new ArgumentNullException("obj", "obj对象不能为null");
            try
            {
                FileInfo fi = new FileInfo(fileName);
                if (!fi.Directory.Exists)
                    fi.Directory.Create();
                if (fi.Exists)
                    fi.Delete();

                using (StreamWriter sw = new StreamWriter(fileName, false))
                {
                    XmlWriterSettings ws = new XmlWriterSettings();
                    ws.OmitXmlDeclaration = true;
                    ws.Indent = true;

                    using (XmlWriter writer = XmlWriter.Create(sw, ws))
                    {
                        //去除默认命名空间xmlns:xsd和xmlns:xsi
                        XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                        ns.Add(string.Empty, string.Empty);
                        XmlSerializer formatter = new XmlSerializer(obj.GetType());
                        formatter.Serialize(writer, obj, ns);
                        writer.Close();
                    }
                    sw.Close();
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return false;
            }
        }

        /// <summary>
        /// 从XML文件中读取数据
        /// </summary>
        /// <param name="fileName">要读取的文件路径</param>
        /// <param name="type">读取的数据类型</param>
        /// <returns>返回从文件中读取的数据</returns>
        public static object ReadXML(string fileName, Type type)
        {
            if (!File.Exists(fileName))
                return null;

            try
            {
                if (type == null)
                    throw new ArgumentNullException("type", "type不能为null");

                XmlSerializer serializer = new XmlSerializer(type);
                using (StreamReader sr = new StreamReader(fileName, Encoding.UTF8))
                {
                    object obj = serializer.Deserialize(sr);
                    sr.Close();
                    return obj;
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                throw ex;
            }
        }

        /// <summary>
        /// Binary序列化
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <param name="data">数据</param>
        public static bool BinarySerialize(string fileName, object data)
        {
            try
            {
                DirectoryInfo dirinfo = Directory.GetParent(fileName);
                if (!dirinfo.Exists)
                    dirinfo.Create();
                BinaryFormatter binaryFomat = new BinaryFormatter();
                using (FileStream fs = new FileStream(fileName, FileMode.Create))
                {
                    binaryFomat.Serialize(fs, data);
                    fs.Dispose();
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return false;
            }
        }

        /// <summary>
        /// Binary反序列化
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <returns></returns>
        public static object BinaryDeserialize(string fileName)
        {
            if (!File.Exists(fileName))
                return null;

            try
            {
                object obj;
                BinaryFormatter binaryFomat = new BinaryFormatter();
                using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate))
                {
                    obj = binaryFomat.Deserialize(fs);
                    fs.Dispose();
                }
                return obj;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                throw ex;
            }
        }

        /// <summary>
        /// 序列化+压缩
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <param name="data">要保存的数据</param>
        public static bool FileCompression(string fileName, object data)
        {
            DirectoryInfo dirinfo = Directory.GetParent(fileName);
            if (!dirinfo.Exists)
                dirinfo.Create();
            try
            {
                //将文件序列化
                BinaryFormatter binaryFomat = new BinaryFormatter();
                //压缩文件的后缀名可以随意起。创建文件路径为相当路径，Debug下，也可为绝对路径
                using (FileStream filedata = new FileStream(fileName, FileMode.Create, FileAccess.Write))
                {
                    //将创建的文件流压缩
                    GZipStream zip = new GZipStream(filedata, CompressionMode.Compress);
                    binaryFomat.Serialize(zip, data);
                    zip.Close();
                    filedata.Close();
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return false;
                //throw ex;
            }
        }

        /// <summary>
        /// 解压缩+反序列化
        /// </summary>
        /// <param name="fileName">文件路径</param>
        /// <returns></returns>
        public static object FileDeCompression(string fileName)
        {
            if (!File.Exists(fileName))
                return null;
            try
            {
                //将在指定路径下，将以压缩的文件变为一个文件流
                using (FileStream fileCompression = File.OpenRead(fileName))
                {
                    //将文件流解压
                    GZipStream gzp = new GZipStream(fileCompression, CompressionMode.Decompress);
                    //反序列化解压的文件流
                    BinaryFormatter binaryFomat = new BinaryFormatter();
                    object obj = binaryFomat.Deserialize(gzp);
                    gzp.Close();
                    fileCompression.Close();
                    return obj;
                }
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                throw ex;
            }
        }
        #endregion

        #region 数据打包
        /// <summary> 打包数据锁 </summary>
        private static readonly object lockAutomaticDataPackaging = new object();
        /// <summary>
        /// 数据一键打包
        /// </summary>
        /// <param name="isContainData"> 含图像数据 </param>
        /// <returns></returns>
        public static bool AutomaticDataPackaging(bool isContainData)
        {
            List<string> path = new List<string>();
            path.Add(PathTool.AllTypePath);
            path.Add(PathTool.DataLogPath);
            if (isContainData)
            {
                path.Add(PathTool.DataPatternImagePath);
                path.Add(PathTool.DataCheckerBoardImagePath);
                path.Add(PathTool.DataCapturePath);
                path.Add(PathTool.DataCalibrationPath);
                path.Add(PathTool.DataReconstructPath);
            }
            lock (lockAutomaticDataPackaging)
            {
                return AutomaticDataPackaging(path.ToArray());
            }
        }
        /// <summary>
        /// 数据一键打包
        /// </summary>
        /// <param name="DataPaths"> 路径 </param>
        /// <returns></returns>
        private static bool AutomaticDataPackaging(string[] DataPaths)
        {
            try
            {
                Stopwatch sw = new Stopwatch();
                sw.Start();

                //创建临时数据文件夹
                Machine.StatusBarAction?.Invoke(0.05, $"初始化", Color.Black);
                string date = DateTime.Now.ToString($"yyyyMMdd_HHmmss");
                string tempPath = PathTool.DataPackageTempPath + date;
                if (!Directory.Exists(tempPath))
                    Directory.CreateDirectory(tempPath);

                //复制文件
                Machine.StatusBarAction?.Invoke(0.1, $"复制数据中", Color.Black);
                int sum = 0;
                foreach (string copyPath in DataPaths)
                {
                    DirectoryInfo di = new DirectoryInfo(copyPath);
                    if (!di.Exists)
                        continue;

                    CopyDirectory(di.FullName, $"{tempPath}\\{di.Name}", false);
                    sum++;
                    Machine.StatusBarAction?.Invoke(0.1 + 0.7 * sum / DataPaths.Length, $"复制数据中（{sum} / {DataPaths.Length}）", Color.Black);
                }

                //压缩打包
                string path = $"{PathTool.DataPackagePath}一键打包数据_{date}.zip";
                ZipFile.CreateFromDirectory(tempPath, path, CompressionLevel.Optimal, true);
                if (Directory.Exists(PathTool.DataPackageTempPath))
                    Directory.Delete(PathTool.DataPackageTempPath, true);
                Log.SaveLog($"一键打包数据完成，耗时：{sw.ElapsedMilliseconds}ms，路径：{path}", LogType.Run, Color.Black);
                Machine.StatusBarAction?.Invoke(0.9, $"完成", Color.Black);

                if (MessageBox.Show($"是否要打开打包数据文件夹（{PathTool.DataPackagePath}）？", $"提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information) == DialogResult.Yes)
                    OpenFolder(PathTool.DataPackagePath);
                Machine.StatusBarAction?.Invoke(0, "", Color.Black);
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex, $"一键打包数据异常，{ex.Message}");
                return false;
            }
        }
        #endregion


        /// <summary>
        /// 类深复制
        /// </summary>
        public static T CloneClass<T>(T source)
        {
            if (Object.ReferenceEquals(source, null))
                return default(T);

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }

        /// <summary>
        /// 打开文件夹
        /// </summary>
        /// <param name="path">完整路径</param>
        /// <param name="isCreate">不存在是否新建？</param>
        /// <returns></returns>
        public static bool OpenFolder(string path, bool isCreate = false)
        {
            try
            {
                DirectoryInfo dir = new DirectoryInfo(path);
                if (!dir.Exists)
                {
                    if (isCreate)
                        dir.Create();
                    if (!dir.Exists)
                        return false;
                }
                System.Diagnostics.Process.Start(dir.FullName);
                return true;
            }
            catch (Exception ex)
            {
                Log.SaveError(ex);
                return false;
            }
        }
    }
}
